
// 原理：红黑树、回调机制、就绪队列
// 数据就绪后经由回调机制将红黑树的节点放入就绪队列中，无须遍历，效率提高
// 检测是否有事件就绪只须判断就绪队列是否为空即可，时间复杂度O(1)

// 关键1.用户告诉内核：你要帮我关心哪一个fd上的哪一个事件
// 关键2.内核告诉用户：你关心的哪些fd上的哪些事件就绪了

// int epoll_create(int size);
// size参数忽略，但必须大于0
// 返回值为epoll的fd，不用时记得close
// 作用为创建epoll模型

// int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
// 解决关键1.用户告诉内核：你要帮我关心哪一个fd上的哪一个事件
// 第一个参数为epoll的fd，由epoll_create(int size)返回
// 第二个参数op有三种模式：EPOLL_CTL_ADD(添加) EPOLL_CTL_MOD(修改) EPOLL_CTL_DEL(删除)
// 底层的红黑树保证其效率优秀
// 第二个参数即为哪一个fd
// 第三个参数event表示哪一个事件，结构体内容如下：
// struct epoll_event
// {
//     uint32_t events;   /* Epoll events */
//     epoll_data_t data; /* User data variable */
// };
// typedef union epoll_data
// {
//     void *ptr;
//     int fd;
//     uint32_t u32;
//     uint64_t u64;
// } epoll_data_t;

// int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
// 解决关键2.内核告诉用户：你关心的哪些fd上的哪些事件就绪了
// 第一个参数为epoll的fd，由epoll_create(int size)返回
// 第二个参数为输出型参数
// 第四个参数单位为毫秒，为-1时表示阻塞等待，为0时表示非阻塞

// 事件：
// EPOLLIN(表示对应的文件描述符可读)
// EPOLLOUT(表示对应的文件描述符可写)
// EPOLLPRI(表示对应的文件描述符有紧急数据可读)

// TCP的PSH标志位本质就是在操作系统层让sockfd的文件数据处于就绪状态

#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include "sock.hpp"
#include "epoller.hpp"
#include "util.hpp"
#include "service.hpp"

namespace cloud
{
    class connection;
    using cb_t = std::function<void (connection*)>;

    class connection
    {
    public:
        connection(){}
        ~connection(){}

        void init(cb_t recver, cb_t sender, cb_t excepter)
        {
            _recver = recver;
            _sender = sender;
            _excepter = excepter;
        }

    public:
        // IO
        int _fd;
        std::string _inbuffer;
        std::string _outbuffer;

        // 回调函数
        cb_t _recver;
        cb_t _sender;
        cb_t _excepter;

        // 事件
        uint32_t _events;
    };

    class epoll_server
    {
        const size_t N = 1024;

    public:
        epoll_server(uint16_t port = 8888)
        :_port(port)
        ,_eventss(std::vector<epoll_event>(N))
        {}

        ~epoll_server()
        {
            _listen_sock.Close();
            _epoller.Close();
        }

        void init()
        {
            _listen_sock.Socket();
            _listen_sock.Bind(_port);
            _listen_sock.Listen();

            _epoller.Create();
            add_conn(_listen_sock.GetSock(), EPOLLIN | EPOLLET);//ET模式倒闭程序员一次读/写完就绪事件
            //std::cout << "初始化完成" << std::endl;
            INFO("初始化完成");
        }

        // 事件派发器
        void dispatch()
        {
            while(1)
            {
                loop_once();
            }
        }

        void loop_once()
        {
            int n = epoll_wait(_epoller.GetFd(), &_eventss[0], N, 0);
            for(size_t i = 0; i < N; ++i)
            {
                int fd = _eventss[i].data.fd;
                uint32_t events = _eventss[i].events;

                if((events & EPOLLERR) || (events & EPOLLHUP))
                    events |= (EPOLLIN | EPOLLOUT);//将所有异常转化为recv和send异常
                if((events & EPOLLIN) && conn_is_exist(fd))
                    _conns[fd]->_recver(_conns[fd]);
                if((events & EPOLLOUT) && conn_is_exist(fd))
                    _conns[fd]->_sender(_conns[fd]);
            }
            //INFO("loop once");
        }

        // 连接管理器
        void accepter(connection *conn)
        {
            // std::cout << "新连接" << std::endl;
            // INFO("新连接,fd为%d", conn->_fd);
            do
            {
                std::string client_ip;
                uint16_t client_port;
                int err_code = 0;
                int sock = _listen_sock.Accept(&client_ip, &client_port, &err_code);
                if(sock > 0)
                {
                    add_conn(sock, EPOLLIN | EPOLLET);
                }
                else
                {
                    if(err_code == EAGAIN || err_code == EWOULDBLOCK)
                    {
                        // 因非阻塞导致错误码被设置，说明事件处理完了，可结束循环
                        // std::cout << "新连接完毕" << std::endl;
                        break;
                    }
                    else if(err_code == EINTR)
                    {
                        // 因信号导致错误码被设置，不能保证事件处理完了，不可结束循环
                        continue;
                    }
                    else
                    {
                        // 连接出错
                        // perror("accept fail");
                        ERROR("连接出错: %s", strerror(errno));
                        continue;
                    }
                }
                
            } while (conn->_events & EPOLLET);//若为ET模式，须将就绪连接的事件处理完为止
        }

        void recver(connection *conn)
        {
            // std::cout << "读取事件就绪" << std::endl;
            // INFO("读事件就绪,fd:%d", conn->_fd);
            int flag = 0;//标志conn是否因异常处理了
            do
            {
                std::string buffer(4096, 0);
                int n = recv(conn->_fd, &buffer[0], buffer.size(), 0);
                if(n > 0)
                {
                    conn->_inbuffer += buffer.substr(0, n);
                    // std::cout << "读取后conn->_inbuffer："<< conn->_inbuffer << std::endl;
                }
                else if(n == 0)
                {
                    // 对方执行有序关闭后，recv的返回值为0
                    // 因非阻塞导致n等于0，说明事件处理完了，可结束循环
                    // 既然对方执行关闭了，不要忘了关闭fd
                    // std::cout << "fd:" << conn->_fd << "有序退出" << std::endl;
                    INFO("fd:%d有序退出", conn->_fd);
                    conn->_excepter(conn);
                    flag = 1;
                    break;
                }
                else
                {
                    if(errno == EAGAIN || errno == EWOULDBLOCK)
                    {
                        // 因非阻塞导致错误码被设置，说明事件处理完了，可结束循环
                        // std::cout << "就绪数据读取完毕" << std::endl;
                        // INFO("就绪数据读取完毕");
                        break;
                    }
                    else if(errno == EINTR)
                    {
                        // 因信号导致错误码被设置，不能保证事件处理完了，不可结束循环
                        INFO("信号导致错误码被设置");
                        continue;
                    }
                    else
                    {
                        // 读取出错
                        // perror("recv fail");
                        ERROR("读取出错: %s", strerror(errno));
                        conn->_excepter(conn);
                        flag = 1;
                        break;
                    }
                }
            } while (conn->_events & EPOLLET);//若为ET模式，须将就绪数据读取完为止
            
            if(flag == 0 && conn->_inbuffer.size() != 0)
            {
                // 应用层协议处理
                if(handle_request(conn) == true)
                {
                    //std::cout << "应用层协议处理后conn->_outbuffer：" << conn->_outbuffer << std::endl;

                    // 一般面对写入的时候，第一次直接写入，没写完再交给epoll
                    // 且读事件常设置，写事件按需设置
                    if(!conn->_outbuffer.empty())
                        conn->_sender(conn);
                }
            }
        }

        void sender(connection *conn)
        {
            int flag = 0;//标志写入是否发生异常
            //std::cout << "写入事件就绪:" << std::endl;
            //std::cout << conn->_outbuffer << std::endl;
            // INFO("写事件就绪，写缓存区内容为：%s", conn->_outbuffer.c_str());
            do
            {
                int n = send(conn->_fd, &conn->_outbuffer[0], conn->_outbuffer.size(), 0);
                if(n > 0)
                {
                    //std::cout << "写入：" << conn->_outbuffer << std::endl;
                    conn->_outbuffer.erase(0, n);
                    if(conn->_outbuffer.empty())
                    {
                        //std::cout << "写入事件处理完毕" << std::endl;
                        break;
                    }   
                }
                else
                {
                    if(errno == EAGAIN || errno == EWOULDBLOCK)
                    {
                        // std::cout << "写入事件处理完毕" << std::endl;
                        // INFO("写入事件处理完毕");
                        break;
                    }
                    else if(errno == EINTR)
                    {
                        continue;
                    }
                    else
                    {
                        // std::cout << "发送错误：" << strerror(errno) << std::endl;
                        ERROR("发送错误: %s", strerror(errno));
                        conn->_excepter(conn);
                        flag = 1;
                        break;
                    }
                }
            } while (conn->_events & EPOLLET);

            if(flag == 0)
            {
                if(conn->_outbuffer.empty())
                    enable_read_write(conn, true, false);
                else
                    enable_read_write(conn, true, true);
            }
        }

        void excepter(connection *conn)
        {
            // std::cout << "已在epoll中移除fd：" << conn->_fd << std::endl;
            INFO("已在epoll中移除fd:%d", conn->_fd);
            // 在epoll中移除fd
            _epoller.DelEvent(conn->_fd);
            // 移除conns中的元素
            _conns.erase(conn->_fd);
            // 关闭fd
            close(conn->_fd);
            // 释放conn，可用智能指针取代
            delete conn;
        }

        // 处理请求
        bool handle_request(connection *conn)
        {
            // 获取完整报文
            // 分离报头
            // 反序列化
            // 业务处理
            // 序列化
            // 添加报头

            //util::get_req_test(conn->_inbuffer);
            
            Request req = util::fill_request(conn->_inbuffer);
            if(req._url.size() == 0 || req._content.size() < req._content_len)
            {
                //std::cout << "没有读完" << std::endl;
                return false;
            }
            else
            {
                INFO("读取到一个完整报文");
                // req.print();
                conn->_inbuffer.clear();
            }
            // req.print();
            INFO("url:%s", req._url.c_str());
            Response res = service::fill_response(req);
            // res.print();
            std::string se_res = util::serialize(res);
            // std::cout << se_res << std::endl;
            conn->_outbuffer += se_res;
            return true;
        }

        // 添加连接
        void add_conn(int fd, uint32_t events)
        {
            // 若为ET模式，设置fd为非阻塞
            if(events & EPOLLET)
                util::set_non_block(fd);
            connection *conn = new connection();
            conn->_fd = fd;
            conn->_events = events;
            if(fd == _listen_sock.GetSock())
            {
                // _listen_sock的recver方法替换为accepter
                conn->init(std::bind(&epoll_server::accepter, this, std::placeholders::_1), nullptr, nullptr);
            }
            else
            {
                conn->init(std::bind(&epoll_server::recver, this, std::placeholders::_1),
                        std::bind(&epoll_server::sender, this, std::placeholders::_1),
                        std::bind(&epoll_server::excepter, this, std::placeholders::_1));
            }
            
            // 将conn管理起来
            _conns.insert(std::pair<int, connection*>(fd, conn));

            // 将要关心的事件写入内核
            _epoller.AddEvent(fd, events);

            INFO("新连接添加成功，fd为%d", conn->_fd);
        }

        void enable_read_write(connection *conn, bool read_flag, bool write_flag)
        {
            uint32_t events = 0;
            if(read_flag == true)
                events |= EPOLLIN;
            if(write_flag == true)
                events |= EPOLLOUT;
            conn->_events = events | EPOLLET;
            _epoller.ModEvent(conn->_fd, conn->_events);
        }

        bool conn_is_exist(int fd)
        {
            return _conns.find(fd) != _conns.end();
        }

    private:
        uint16_t _port;                               // 端口号
        sock _listen_sock;                            // 封装的socket类
        epoller _epoller;                             // 封装的epoll类
        std::vector<epoll_event> _eventss;            // 事件数组
        std::unordered_map<int, connection*> _conns;  // 连接哈希表
    };
}